import irrlicht;
import std.stdio;
import std.path;
import std.string;

__gshared IrrlichtDevice Device;
__gshared string StartUpModelFile;
__gshared wstring MessageText;
__gshared wstring Caption;
__gshared ISceneNode Model;
__gshared ISceneNode SkyBox;
__gshared bool Octree = false;
__gshared bool UseLight = false;

ICameraSceneNode[2] Camera = [null, null];

enum
{
    GUI_ID_DIALOG_ROOT_WINDOW = 0x10000,
    
    GUI_ID_X_SCALE,
    GUI_ID_Y_SCALE,
    GUI_ID_Z_SCALE,

    GUI_ID_OPEN_MODEL,
    GUI_ID_SET_MODEL_ARCHIVE,
    GUI_ID_LOAD_AS_OCTREE,

    GUI_ID_SKY_BOX_VISIBLE,
    GUI_ID_TOGGLE_DEBUG_INFO,

    GUI_ID_DEBUG_OFF,
    GUI_ID_DEBUG_BOUNDING_BOX,
    GUI_ID_DEBUG_NORMALS,
    GUI_ID_DEBUG_SKELETON,
    GUI_ID_DEBUG_WIRE_OVERLAY,
    GUI_ID_DEBUG_HALF_TRANSPARENT,
    GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES,
    GUI_ID_DEBUG_ALL,

    GUI_ID_MODEL_MATERIAL_SOLID,
    GUI_ID_MODEL_MATERIAL_TRANSPARENT,
    GUI_ID_MODEL_MATERIAL_REFLECTION,

    GUI_ID_CAMERA_MAYA,
    GUI_ID_CAMERA_FIRST_PERSON,

    GUI_ID_POSITION_TEXT,

    GUI_ID_ABOUT,
    GUI_ID_QUIT,

    GUI_ID_TEXTUREFILTER,
    GUI_ID_SKIN_TRANSPARENCY,
    GUI_ID_SKIN_ANIMATION_FPS,

    GUI_ID_BUTTON_SET_SCALE,
    GUI_ID_BUTTON_SCALE_MUL10,
    GUI_ID_BUTTON_SCALE_DIV10,
    GUI_ID_BUTTON_OPEN_MODEL,
    GUI_ID_BUTTON_SHOW_ABOUT,
    GUI_ID_BUTTON_SHOW_TOOLBOX,
    GUI_ID_BUTTON_SELECT_ARCHIVE,

    // And some magic numbers
    MAX_FRAMERATE = 1000,
    DEFAULT_FRAMERATE = 30
}

void setActiveCamera(ICameraSceneNode newActive)
{
    if(!Device)
        return;
    
    ICameraSceneNode active = Device.getSceneManager().getActiveCamera();
    active.setInputReceiverEnabled(false);
    
    newActive.setInputReceiverEnabled(true);
    Device.getSceneManager().setActiveCamera(newActive);
}

void SetSkinTransparency(int alpha, IGUISkin skin)
{
    for(int i = 0; i < EGUI_DEFAULT_COLOR.EGDC_COUNT; ++i)
    {
        SColor col = skin.getColor(cast(EGUI_DEFAULT_COLOR)i);
        col.setAlpha(alpha);
        skin.setColor(cast(EGUI_DEFAULT_COLOR)i, col);
    }
}

void UpdateScaleInfo(ISceneNode model)
{
    IGUIElement toolboxWnd = Device.getGUIEnvironment().getRootGUIElement().getElementFromId(GUI_ID_DIALOG_ROOT_WINDOW, true);
    if(!toolboxWnd)
        return;
    if(!model)
    {
        toolboxWnd.getElementFromId(GUI_ID_X_SCALE, true).setText("-");
        toolboxWnd.getElementFromId(GUI_ID_Y_SCALE, true).setText("-");
        toolboxWnd.getElementFromId(GUI_ID_Z_SCALE, true).setText("-");
    }
    else
    {
        FloatVector3D scale = model.getScale();
        toolboxWnd.getElementFromId(GUI_ID_X_SCALE, true).setText(std.conv.to!wstring(scale.X));
        toolboxWnd.getElementFromId(GUI_ID_Y_SCALE, true).setText(std.conv.to!wstring(scale.Y));
        toolboxWnd.getElementFromId(GUI_ID_Z_SCALE, true).setText(std.conv.to!wstring(scale.Z));
    }
}

void showAboutText()
{
    Device.getGUIEnvironment.addMessageBox(Caption, MessageText);
}

void loadModel(string filename)
{
    string extension = getExt(filename);
    extension = extension.tolower();
    
    if(extension == ".jpg" || extension == ".pcx" ||
      extension == ".png" || extension == ".ppm" ||
      extension == ".pgm" || extension == ".pbm" ||
      extension == ".psd" || extension == ".tga" ||
      extension == ".bmp" || extension == ".wa" ||
      extension == ".rgb" || extension == ".rgba")
    {
        ITexture texture = Device.getVideoDriver().getTexture(filename);
        if(texture && Model)
        {
            Device.getVideoDriver().removeTexture(texture);
            texture = Device.getVideoDriver().getTexture(filename);
            
            Model.setMaterialTexture(0, texture);
        }
        return;
    }
    else if(extension == ".pk3" || extension == ".zip" || extension == ".pak" || extension == ".npk")
    {
        Device.getFileSystem().addFileArchive(filename);
    }
    
    if(Model)
        Model.remove();
        
    Model = null;
    
    if(extension == ".irr")
    {
        ISceneNode[] outNodes;
        Device.getSceneManager().loadScene(filename);
        Device.getSceneManager().getSceneNodesFromType(ESCENE_NODE_TYPE.ESNT_ANIMATED_MESH, outNodes);
        if(outNodes.length)
            Model = outNodes[0];
        return;
    }
    
    IAnimatedMesh m = Device.getSceneManager().getMesh(filename);
    
    if(!m)
    {
        if(StartUpModelFile != filename)
            Device.getGUIEnvironment().addMessageBox(
                Caption, "The model could not be loaded. " ~
                         "Maybe it is not a supported file format.");
        return;
    }
    
    if(Octree)
        Model = Device.getSceneManager().addOctreeSceneNode(m.getMesh(0));
    else
    {
        IAnimatedMeshSceneNode animModel = Device.getSceneManager().addAnimatedMeshSceneNode(m);
        animModel.setAnimationSpeed(30);
        Model = animModel;
    }
    Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_LIGHTING, UseLight);
    Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_NORMALIZE_NORMALS, UseLight);
    Model.setDebugDataVisible(E_DEBUG_SCENE_TYPE.EDS_OFF);
    
    IGUIContextMenu menu = new IGUIContextMenu(IGUIElement.swigGetCPtr(Device.getGUIEnvironment().getRootGUIElement().getElementFromId(GUI_ID_TOGGLE_DEBUG_INFO, true)), false);
    if(menu)
        for(int item = 1; item < 6; ++item)
            menu.setItemChecked(item, false);
    UpdateScaleInfo(Model);
}

void createToolBox()
{
    IGUIEnvironment env = Device.getGUIEnvironment();
    IGUIElement root = env.getRootGUIElement();
    IGUIElement e = root.getElementFromId(GUI_ID_DIALOG_ROOT_WINDOW, true);
    if(e)
        e.remove();
    
    IGUIWindow wnd = env.addWindow(new IntRect(600,45,800,480), false, "Toolset", null, GUI_ID_DIALOG_ROOT_WINDOW);
    
    IGUITabControl tab = env.addTabControl(new IntRect(2,20,800-602,480-7), wnd, true, true);
    
    IGUITab t1 = tab.addTab("Config");
    
    env.addStaticText("Scale:", new IntRect(10,20,60,45), false, false, t1);
    env.addStaticText("X:", new IntRect(22,48,40,66), false, false, t1);
    env.addEditBox("1.0", new IntRect(40,46,130,66), true, t1, GUI_ID_X_SCALE);
    env.addStaticText("Y:", new IntRect(22,82,40,GUI_ID_OPEN_MODEL), false, false, t1);
    env.addEditBox("1.0", new IntRect(40,76,130,96),true, t1, GUI_ID_Y_SCALE);
    env.addStaticText("Z:", new IntRect(22,108,40,126),false, false, t1);
    env.addEditBox("1.0", new IntRect(40,106,130,126), true, t1, GUI_ID_Z_SCALE);
    
    env.addButton(new IntRect(10,134,85,165), t1, GUI_ID_BUTTON_SET_SCALE, "Set");

    // quick scale buttons
    env.addButton(new IntRect(65,20,95,40), t1, GUI_ID_BUTTON_SCALE_MUL10, "* 10");
    env.addButton(new IntRect(100,20,130,40), t1, GUI_ID_BUTTON_SCALE_DIV10, "* 0.1");

    UpdateScaleInfo(Model);

    // add transparency control
    env.addStaticText("GUI Transparency Control:",
                    new IntRect(10,200,150,225), true, false, t1);
    IGUIScrollBar scrollbar = env.addScrollBar(true,
                    new IntRect(10,225,150,240), t1, GUI_ID_SKIN_TRANSPARENCY);
    scrollbar.setMax(255);
    scrollbar.setPos(255);

    // add framerate control
    env.addStaticText("Framerate:",
                    new IntRect(10,240,150,265), true, false, t1);
    scrollbar = env.addScrollBar(true,
                    new IntRect(10,265,150,280), t1, GUI_ID_SKIN_ANIMATION_FPS);
    scrollbar.setMax(MAX_FRAMERATE);
    scrollbar.setMin(-MAX_FRAMERATE);
    scrollbar.setPos(DEFAULT_FRAMERATE);

    // bring irrlicht engine logo to front, because it
    // now may be below the newly created toolbox
    root.bringToFront(root.getElementFromId(666, true));
}

class MyEventReceiver : IEventReceiver
{
    override bool OnEvent(SEvent event)
    {
        // Escape swaps Camera Input
        if (event.EventType == EEVENT_TYPE.EET_KEY_INPUT_EVENT &&
                event.KeyInput.PressedDown == false)
        {
            if ( OnKeyUp(event.KeyInput.Key) )
                return true;
        }

        if (event.EventType == EEVENT_TYPE.EET_GUI_EVENT)
        {
            int id = event.GUIEvent.Caller.getID();
            IGUIEnvironment env = Device.getGUIEnvironment();

            switch(event.GUIEvent.EventType)
            {
            case EGUI_EVENT_TYPE.EGET_MENU_ITEM_SELECTED:
                // a menu item was clicked
                OnMenuItemSelected( (new IGUIContextMenu(IGUIElement.swigGetCPtr(event.GUIEvent.Caller), false)) );
                break;

            case EGUI_EVENT_TYPE.EGET_FILE_SELECTED:
            {
                // load the model file, selected in the file open dialog
                IGUIFileOpenDialog dialog =
                    (new IGUIFileOpenDialog(IGUIElement.swigGetCPtr(event.GUIEvent.Caller), false));
                loadModel(std.conv.to!string(dialog.getFileName()));
            }
            break;

            case EGUI_EVENT_TYPE.EGET_SCROLL_BAR_CHANGED:
                // control skin transparency
                if (id == GUI_ID_SKIN_TRANSPARENCY)
                {
                    const int pos = (new IGUIScrollBar(IGUIElement.swigGetCPtr(event.GUIEvent.Caller), false)).getPos();
                    SetSkinTransparency(pos, env.getSkin());
                }
                // control animation speed
                else if (id == GUI_ID_SKIN_ANIMATION_FPS)
                {
                    int pos = (new IGUIScrollBar(IGUIElement.swigGetCPtr(event.GUIEvent.Caller), false)).getPos();

                    if (ESCENE_NODE_TYPE.ESNT_ANIMATED_MESH == Model.getType())
                    {
                        auto iamsn = (new IAnimatedMeshSceneNode(ISceneNode.swigGetCPtr(Model), false));
                        iamsn.setAnimationSpeed(pos);
                    }
                }
                break;

            case EGUI_EVENT_TYPE.EGET_COMBO_BOX_CHANGED:

                // control anti-aliasing/filtering
                if (id == GUI_ID_TEXTUREFILTER)
                {
                    OnTextureFilterSelected(new IGUIComboBox(IGUIElement.swigGetCPtr(event.GUIEvent.Caller), false));
                }
                break;

            case EGUI_EVENT_TYPE.EGET_BUTTON_CLICKED:

                switch(id)
                {
                case GUI_ID_BUTTON_SET_SCALE:
                {
                    // set scale
                    IGUIElement root = env.getRootGUIElement();
                    FloatVector3D scale;
                    string s;

                    s = std.conv.to!string(root.getElementFromId(GUI_ID_X_SCALE, true).getText());
                    scale.X = std.conv.to!float(s);
                    s = std.conv.to!string(root.getElementFromId(GUI_ID_Y_SCALE, true).getText());
                    scale.Y = std.conv.to!float(s);
                    s = std.conv.to!string(root.getElementFromId(GUI_ID_Z_SCALE, true).getText());
                    scale.Z = std.conv.to!float(s);

                    if (Model)
                        Model.setScale(scale);
                    UpdateScaleInfo(Model);
                }
                    break;
                case GUI_ID_BUTTON_SCALE_MUL10:
                    if (Model)
                        Model.setScale(Model.getScale()*10.f);
                    UpdateScaleInfo(Model);
                    break;
                case GUI_ID_BUTTON_SCALE_DIV10:
                    if (Model)
                        Model.setScale(Model.getScale()*0.1f);
                    UpdateScaleInfo(Model);
                    break;
                case GUI_ID_BUTTON_OPEN_MODEL:
                    env.addFileOpenDialog("Please select a model file to open");
                    break;
                case GUI_ID_BUTTON_SHOW_ABOUT:
                    showAboutText();
                    break;
                case GUI_ID_BUTTON_SHOW_TOOLBOX:
                    createToolBox();
                    break;
                case GUI_ID_BUTTON_SELECT_ARCHIVE:
                    env.addFileOpenDialog("Please select your game archive/directory");
                    break;
                }  //end of switch(id)

                break;
            default:
                    break;
            }
        }
        return false;
    }
    
    bool OnKeyUp(EKEY_CODE keyCode)
    {
        if (keyCode == EKEY_CODE.KEY_ESCAPE)
        {
            if (Device)
            {
                ICameraSceneNode camera =
                        Device.getSceneManager().getActiveCamera();
                if (camera)
                {
                    camera.setInputReceiverEnabled( !camera.isInputReceiverEnabled() );
                }
                return true;
            }
        }
        else if (keyCode == EKEY_CODE.KEY_F1)
        {
            if (Device)
            {
                IGUIElement elem = Device.getGUIEnvironment().getRootGUIElement().getElementFromId(GUI_ID_POSITION_TEXT);
                if (elem)
                    elem.setVisible(!elem.isVisible());
            }
        }
        else if (keyCode == EKEY_CODE.KEY_KEY_M)
        {
            if (Device)
                Device.minimizeWindow();
        }
        else if (keyCode == EKEY_CODE.KEY_KEY_L)
        {
            UseLight=!UseLight;
            if (Model)
            {
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_LIGHTING, UseLight);
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_NORMALIZE_NORMALS, UseLight);
            }
        }
        return false;
    }
    
    void OnMenuItemSelected( IGUIContextMenu menu )
    {
        int id = menu.getItemCommandId(menu.getSelectedItem());
        IGUIEnvironment env = Device.getGUIEnvironment();

        switch(id)
        {
        case GUI_ID_OPEN_MODEL: // FilOnButtonSetScalinge . Open Model
            env.addFileOpenDialog("Please select a model file to open");
            break;
        case GUI_ID_SET_MODEL_ARCHIVE: // File . Set Model Archive
            env.addFileOpenDialog("Please select your game archive/directory");
            break;
        case GUI_ID_LOAD_AS_OCTREE: // File . LoadAsOctree
            Octree = !Octree;
            menu.setItemChecked(menu.getSelectedItem(), Octree);
            break;
        case GUI_ID_QUIT: // File . Quit
            Device.closeDevice();
            break;
        case GUI_ID_SKY_BOX_VISIBLE: // View . Skybox
            menu.setItemChecked(menu.getSelectedItem(), !menu.isItemChecked(menu.getSelectedItem()));
            SkyBox.setVisible(!SkyBox.isVisible());
            break;
        case GUI_ID_DEBUG_OFF: // View . Debug Information
            menu.setItemChecked(menu.getSelectedItem()+1, false);
            menu.setItemChecked(menu.getSelectedItem()+2, false);
            menu.setItemChecked(menu.getSelectedItem()+3, false);
            menu.setItemChecked(menu.getSelectedItem()+4, false);
            menu.setItemChecked(menu.getSelectedItem()+5, false);
            menu.setItemChecked(menu.getSelectedItem()+6, false);
            if (Model)
                Model.setDebugDataVisible(E_DEBUG_SCENE_TYPE.EDS_OFF);
            break;
        case GUI_ID_DEBUG_BOUNDING_BOX: // View . Debug Information
            menu.setItemChecked(menu.getSelectedItem(), !menu.isItemChecked(menu.getSelectedItem()));
            if (Model)
                Model.setDebugDataVisible(cast(E_DEBUG_SCENE_TYPE)(Model.isDebugDataVisible()^E_DEBUG_SCENE_TYPE.EDS_BBOX));
            break;
        case GUI_ID_DEBUG_NORMALS: // View . Debug Information
            menu.setItemChecked(menu.getSelectedItem(), !menu.isItemChecked(menu.getSelectedItem()));
            if (Model)
                Model.setDebugDataVisible(cast(E_DEBUG_SCENE_TYPE)(Model.isDebugDataVisible()^E_DEBUG_SCENE_TYPE.EDS_NORMALS));
            break;
        case GUI_ID_DEBUG_SKELETON: // View . Debug Information
            menu.setItemChecked(menu.getSelectedItem(), !menu.isItemChecked(menu.getSelectedItem()));
            if (Model)
                Model.setDebugDataVisible(cast(E_DEBUG_SCENE_TYPE)(Model.isDebugDataVisible()^E_DEBUG_SCENE_TYPE.EDS_SKELETON));
            break;
        case GUI_ID_DEBUG_WIRE_OVERLAY: // View . Debug Information
            menu.setItemChecked(menu.getSelectedItem(), !menu.isItemChecked(menu.getSelectedItem()));
            if (Model)
                Model.setDebugDataVisible(cast(E_DEBUG_SCENE_TYPE)(Model.isDebugDataVisible()^E_DEBUG_SCENE_TYPE.EDS_MESH_WIRE_OVERLAY));
            break;
        case GUI_ID_DEBUG_HALF_TRANSPARENT: // View . Debug Information
            menu.setItemChecked(menu.getSelectedItem(), !menu.isItemChecked(menu.getSelectedItem()));
            if (Model)
                Model.setDebugDataVisible(cast(E_DEBUG_SCENE_TYPE)(Model.isDebugDataVisible()^E_DEBUG_SCENE_TYPE.EDS_HALF_TRANSPARENCY));
            break;
        case GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES: // View . Debug Information
            menu.setItemChecked(menu.getSelectedItem(), !menu.isItemChecked(menu.getSelectedItem()));
            if (Model)
                Model.setDebugDataVisible(cast(E_DEBUG_SCENE_TYPE)(Model.isDebugDataVisible()^E_DEBUG_SCENE_TYPE.EDS_BBOX_BUFFERS));
            break;
        case GUI_ID_DEBUG_ALL: // View . Debug Information
            menu.setItemChecked(menu.getSelectedItem()-1, true);
            menu.setItemChecked(menu.getSelectedItem()-2, true);
            menu.setItemChecked(menu.getSelectedItem()-3, true);
            menu.setItemChecked(menu.getSelectedItem()-4, true);
            menu.setItemChecked(menu.getSelectedItem()-5, true);
            menu.setItemChecked(menu.getSelectedItem()-6, true);
            if (Model)
                Model.setDebugDataVisible(E_DEBUG_SCENE_TYPE.EDS_FULL);
            break;
        case GUI_ID_ABOUT: // Help.About
            showAboutText();
            break;
        case GUI_ID_MODEL_MATERIAL_SOLID: // View . Material . Solid
            if (Model)
                Model.setMaterialType(E_MATERIAL_TYPE.EMT_SOLID);
            break;
        case GUI_ID_MODEL_MATERIAL_TRANSPARENT: // View . Material . Transparent
            if (Model)
                Model.setMaterialType(E_MATERIAL_TYPE.EMT_TRANSPARENT_ADD_COLOR);
            break;
        case GUI_ID_MODEL_MATERIAL_REFLECTION: // View . Material . Reflection
            if (Model)
                Model.setMaterialType(E_MATERIAL_TYPE.EMT_SPHERE_MAP);
            break;

        case GUI_ID_CAMERA_MAYA:
            setActiveCamera(Camera[0]);
            break;
        case GUI_ID_CAMERA_FIRST_PERSON:
            setActiveCamera(Camera[1]);
            break;
        }
    }
    
    void OnTextureFilterSelected( IGUIComboBox combo )
    {
        int pos = combo.getSelected();
        switch (pos)
        {
            case 0:
            if (Model)
            {
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_BILINEAR_FILTER, false);
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_TRILINEAR_FILTER, false);
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_ANISOTROPIC_FILTER, false);
            }
            break;
            case 1:
            if (Model)
            {
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_BILINEAR_FILTER, true);
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_TRILINEAR_FILTER, false);
            }
            break;
            case 2:
            if (Model)
            {
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_BILINEAR_FILTER, false);
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_TRILINEAR_FILTER, true);
            }
            break;
            case 3:
            if (Model)
            {
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_ANISOTROPIC_FILTER, true);
            }
            break;
            case 4:
            if (Model)
            {
                Model.setMaterialFlag(E_MATERIAL_FLAG.EMF_ANISOTROPIC_FILTER, false);
            }
            break;
        }
    }
}

void main(string[] args)
{
    E_DRIVER_TYPE driverType = driverChoiceConsole();
    assert(driverType != E_DRIVER_TYPE.EDT_COUNT);
    
        
    MyEventReceiver receiver = new MyEventReceiver();
    Device = createDevice(driverType, new UintDimension2D(800,600), 16, false, false, false, receiver);
    
    assert(Device, "Unable to create device.");
    
    Device.setResizable(true);
    
    Device.setWindowCaption("Irrlicht Engine - Loading...");
    
    IVideoDriver driver = Device.getVideoDriver();
    IGUIEnvironment env = Device.getGUIEnvironment();
    ISceneManager smgr = Device.getSceneManager();
    smgr.getParameters().setAttribute(COLLADA_CREATE_SCENE_INSTANCES, true);
    
    driver.setTextureCreationFlag(E_TEXTURE_CREATION_FLAG.ETCF_ALWAYS_32_BIT, true);
    
    smgr.addLightSceneNode(null, new FloatVector3D(200,200,200), new SColorf(1,1,1), 2000);
    
    smgr.setAmbientLight(new SColorf(.3,.3,.3));
    
    Device.getFileSystem().addFolderFileArchive("media/");
    
    // read configuration from xml file

    IXMLReader xml = Device.getFileSystem().createXMLReader( "config.xml");

    while(xml && xml.read())
    {
        switch(xml.getNodeType())
        {
        case EXML_NODE.EXN_TEXT:
            // in this xml file, the only text which occurs is the
            // messageText
            MessageText = xml.getNodeData();
            break;
        case EXML_NODE.EXN_ELEMENT:
            {
                if ("startUpModel"w == xml.getNodeName())
                    StartUpModelFile = std.conv.to!string(xml.getAttributeValue("file"));
                else
                if ("messageText"w == xml.getNodeName())
                    Caption = xml.getAttributeValue("caption");
            }
            break;
        default:
            break;
        }
    }


    if (args.length > 1)
        StartUpModelFile = args[1];
        
    // set a nicer font

    IGUISkin skin = env.getSkin();
    IGUIFont font = env.getFont("fonthaettenschweiler.bmp");
    if (font)
        skin.setFont(font);

    // create menu
    IGUIContextMenu menu = env.addMenu();
    menu.addItem("File", -1, true, true);
    menu.addItem("View", -1, true, true);
    menu.addItem("Camera", -1, true, true);
    menu.addItem("Help", -1, true, true);

    IGUIContextMenu submenu;
    submenu = menu.getSubMenu(0);
    submenu.addItem("Open Model File & Texture...", GUI_ID_OPEN_MODEL);
    submenu.addItem("Set Model Archive...", GUI_ID_SET_MODEL_ARCHIVE);
    submenu.addItem("Load as Octree", GUI_ID_LOAD_AS_OCTREE);
    submenu.addSeparator();
    submenu.addItem("Quit", GUI_ID_QUIT);

    submenu = menu.getSubMenu(1);
    submenu.addItem("sky box visible", GUI_ID_SKY_BOX_VISIBLE, true, false, true);
    submenu.addItem("toggle model debug information", GUI_ID_TOGGLE_DEBUG_INFO, true, true);
    submenu.addItem("model material", -1, true, true );

    submenu = submenu.getSubMenu(1);
    submenu.addItem("Off", GUI_ID_DEBUG_OFF);
    submenu.addItem("Bounding Box", GUI_ID_DEBUG_BOUNDING_BOX);
    submenu.addItem("Normals", GUI_ID_DEBUG_NORMALS);
    submenu.addItem("Skeleton", GUI_ID_DEBUG_SKELETON);
    submenu.addItem("Wire overlay", GUI_ID_DEBUG_WIRE_OVERLAY);
    submenu.addItem("Half-Transparent", GUI_ID_DEBUG_HALF_TRANSPARENT);
    submenu.addItem("Buffers bounding boxes", GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES);
    submenu.addItem("All", GUI_ID_DEBUG_ALL);

    submenu = menu.getSubMenu(1).getSubMenu(2);
    submenu.addItem("Solid", GUI_ID_MODEL_MATERIAL_SOLID);
    submenu.addItem("Transparent", GUI_ID_MODEL_MATERIAL_TRANSPARENT);
    submenu.addItem("Reflection", GUI_ID_MODEL_MATERIAL_REFLECTION);

    submenu = menu.getSubMenu(2);
    submenu.addItem("Maya Style", GUI_ID_CAMERA_MAYA);
    submenu.addItem("First Person", GUI_ID_CAMERA_FIRST_PERSON);

    submenu = menu.getSubMenu(3);
    submenu.addItem("About", GUI_ID_ABOUT);
    
    
    // create toolbar

    IGUIToolBar bar = env.addToolBar();

    ITexture image = driver.getTexture("open.png");
    bar.addButton(GUI_ID_BUTTON_OPEN_MODEL, null, "Open a model",image, null, false, true);

    image = driver.getTexture("tools.png");
    bar.addButton(GUI_ID_BUTTON_SHOW_TOOLBOX, null, "Open Toolset",image, null, false, true);

    image = driver.getTexture("zip.png");
    bar.addButton(GUI_ID_BUTTON_SELECT_ARCHIVE, null, "Set Model Archive",image, null, false, true);

    image = driver.getTexture("help.png");
    bar.addButton(GUI_ID_BUTTON_SHOW_ABOUT, null, "Open Help", image, null, false, true);

    // create a combobox with some senseless texts

    IGUIComboBox box = env.addComboBox(new IntRect(250,4,350,23), bar, GUI_ID_TEXTUREFILTER);
    box.addItem("No filtering");
    box.addItem("Bilinear");
    box.addItem("Trilinear");
    box.addItem("Anisotropic");
    box.addItem("Isotropic");
    
    // disable alpha

    for (int i=0; i<EGUI_DEFAULT_COLOR.EGDC_COUNT ; ++i)
    {
            SColor col = env.getSkin().getColor(cast(EGUI_DEFAULT_COLOR)i);
            col.setAlpha(255);
            env.getSkin().setColor(cast(EGUI_DEFAULT_COLOR)i, col);
    }

    // add a tabcontrol

    createToolBox();

    // create fps text

    IGUIStaticText fpstext = env.addStaticText("",
                    new IntRect(400,4,570,23), true, false, bar);

    IGUIStaticText postext = env.addStaticText("",
                    new IntRect(10,50,470,80),false, false, null, GUI_ID_POSITION_TEXT);
    postext.setVisible(false);

    // set window caption

    Caption ~= " - [";
    Caption ~= driver.getName();
    Caption ~= "]";
    Device.setWindowCaption(Caption);
    
    if(args.length == 1)
        showAboutText();
    loadModel(StartUpModelFile);
    
    SkyBox = smgr.addSkyBoxSceneNode(
                driver.getTexture("irrlicht2_up.jpg"),
                driver.getTexture("irrlicht2_dn.jpg"),
                driver.getTexture("irrlicht2_lf.jpg"),
                driver.getTexture("irrlicht2_rt.jpg"),
                driver.getTexture("irrlicht2_ft.jpg"),
                driver.getTexture("irrlicht2_bk.jpg"));

    // add a camera scene node
    Camera[0] = smgr.addCameraSceneNodeMaya();
    Camera[0].setFarValue(20000);
    // Maya cameras reposition themselves relative to their target, so target the location
    // where the mesh scene node is placed.
    Camera[0].setTarget(new FloatVector3D(0,30,0));

    Camera[1] = smgr.addCameraSceneNodeFPS();
    Camera[1].setFarValue(20000);
    Camera[1].setPosition(new FloatVector3D(0,0,-70));
    Camera[1].setTarget(new FloatVector3D(0,30,0));

    setActiveCamera(Camera[0]);

    // load the irrlicht engine logo
    IGUIImage img =
            env.addImage(driver.getTexture("irrlichtlogo2.png"),
                    new IntVector2D(10, driver.getScreenSize().Height - 128));

    // lock the logo's edges to the bottom left corner of the screen
    img.setAlignment(EGUI_ALIGNMENT.EGUIA_UPPERLEFT, EGUI_ALIGNMENT.EGUIA_UPPERLEFT,
                    EGUI_ALIGNMENT.EGUIA_LOWERRIGHT, EGUI_ALIGNMENT.EGUIA_LOWERRIGHT);

    // draw everything

    while(Device.run() && driver)
    {
        if (Device.isWindowActive())
        {
            driver.beginScene(true, true, new SColor(150,50,50,50));

            smgr.drawAll();
            env.drawAll();

            driver.endScene();

            wstring str = "FPS";
            str ~= std.conv.to!wstring(driver.getFPS());
            str ~= " Tris: ";
            str ~= std.conv.to!wstring((driver.getPrimitiveCountDrawn()));
            fpstext.setText(str);

            ICameraSceneNode cam = Device.getSceneManager().getActiveCamera();
            str = "Pos: ";
            str ~= std.conv.to!wstring(cam.getPosition().X);
            str ~= " ";
            str ~= std.conv.to!wstring(cam.getPosition().Y);
            str ~= " ";
            str ~= std.conv.to!wstring(cam.getPosition().Z);
            str ~= " Tgt: ";
            str ~= std.conv.to!wstring(cam.getTarget().X);
            str ~= " ";
            str ~= std.conv.to!wstring(cam.getTarget().Y);
            str ~= " ";
            str ~= std.conv.to!wstring(cam.getTarget().Z);
            postext.setText(str);
        }
        else
            Device.yield();
    }

    return;
}